<?php

namespace Kunfeng\Dify\Client\Impl;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Kunfeng\Dify\Callback\ChatStreamCallback;
use Kunfeng\Dify\Client\DifyChatClientInterface;
use Kunfeng\Dify\Constants\ApiPaths;
use Kunfeng\Dify\Enum\ResponseMode;
use Kunfeng\Dify\Event\EventFactory;
use Kunfeng\Dify\Exception\DifyApiException;
use Kunfeng\Dify\Model\Chat\ChatMessage;
use Kunfeng\Dify\Model\Chat\ChatMessageResponse;
use Kunfeng\Dify\Model\Chat\AppInfoResponse;
use Kunfeng\Dify\Model\Chat\AppParametersResponse;
use Kunfeng\Dify\Model\Chat\AppWebAppSettingResponse;
use Kunfeng\Dify\Model\Chat\AppMetaResponse;
use Kunfeng\Dify\Model\Chat\MessageListResponse;
use Kunfeng\Dify\Model\Chat\ConversationListResponse;
use Kunfeng\Dify\Model\Chat\Conversation;
use Kunfeng\Dify\Model\Chat\SuggestedQuestionsResponse;
use Kunfeng\Dify\Model\Chat\AudioToTextResponse;
use Kunfeng\Dify\Model\Chat\AnnotationListResponse;
use Kunfeng\Dify\Model\Chat\Annotation;
use Kunfeng\Dify\Model\Chat\AnnotationReply;
use Kunfeng\Dify\Model\Chat\VariableResponse;
use Kunfeng\Dify\Model\Common\SimpleResponse;
use Kunfeng\Dify\Model\File\FileUploadRequest;
use Kunfeng\Dify\Model\File\FileUploadResponse;
use Kunfeng\Dify\Model\File\FilePreviewResponse;

/**
 * Dify 对话型应用客户端实现
 */
class DifyChatClient extends AbstractDifyClient implements DifyChatClientInterface
{
    // 使用常量类中的API路径

    public function __construct(string $baseUrl, string $apiKey, $httpClient = null, $retryHandler = null)
    {
        parent::__construct($baseUrl, $apiKey, $httpClient, $retryHandler);
    }

    /**
     * 发送对话消息（阻塞模式）
     */
    public function sendChatMessage(ChatMessage $message): ChatMessageResponse
    {
        $data = $this->executePost(ApiPaths::CHAT_MESSAGES, $message->toArray());
        return ChatMessageResponse::fromArray($data);
    }

    /**
     * 发送对话消息（流式模式）
     */
    public function sendChatMessageStream(ChatMessage $message, ChatStreamCallback $callback): void
    {
        // 确保请求模式为流式
        $messageData = $message->toArray();
        $messageData['response_mode'] = ResponseMode::STREAMING;

        try {
            $response = $this->httpClient->request('POST', $this->baseUrl . ApiPaths::CHAT_MESSAGES, [
                'json' => $messageData,
                'stream' => true,
                'headers' => [
                    'Accept' => 'text/event-stream',
                ]
            ]);

            $this->processStreamResponse($response, $callback);
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
            $callback->handleException($e);
        }
    }

    /**
     * 停止对话消息
     */
    public function stopChatMessage(string $taskId, string $user): SimpleResponse
    {
        $data = $this->executePost(
            ApiPaths::CHAT_MESSAGES . '/' . $taskId . ApiPaths::STOP,
            ['user' => $user]
        );
        return SimpleResponse::fromArray($data);
    }

    /**
     * 消息反馈（点赞）
     */
    public function feedbackMessage(string $messageId, string $rating, string $user, ?string $content = null): SimpleResponse
    {
        $body = [
            'rating' => $rating,
            'user' => $user,
        ];
        
        if ($content !== null) {
            $body['content'] = $content;
        }

        $data = $this->executePost(
            ApiPaths::MESSAGES . '/' . $messageId . ApiPaths::FEEDBACKS,
            $body
        );
        return SimpleResponse::fromArray($data);
    }

    /**
     * 获取下一轮建议问题列表
     */
    public function getSuggestedQuestions(string $messageId, string $user): SuggestedQuestionsResponse
    {
        $data = $this->executeGet(
            ApiPaths::MESSAGES . '/' . $messageId . ApiPaths::SUGGESTED_QUESTIONS,
            ['user' => $user]
        );
        return SuggestedQuestionsResponse::fromArray($data);
    }

    /**
     * 获取会话历史消息
     */
    public function getMessages(string $conversationId, string $user, ?string $firstId = null, ?int $limit = null): MessageListResponse
    {
        $query = [
            'conversation_id' => $conversationId,
            'user' => $user,
        ];

        if ($firstId !== null) {
            $query['first_id'] = $firstId;
        }
        if ($limit !== null) {
            $query['limit'] = $limit;
        }

        $data = $this->executeGet(ApiPaths::MESSAGES, $query);
        return MessageListResponse::fromArray($data);
    }

    /**
     * 获取会话列表
     */
    public function getConversations(string $user, ?string $lastId = null, ?int $limit = null, ?string $sortBy = null): ConversationListResponse
    {
        $query = ['user' => $user];

        if ($lastId !== null) {
            $query['last_id'] = $lastId;
        }
        if ($limit !== null) {
            $query['limit'] = $limit;
        }
        if ($sortBy !== null) {
            $query['sort_by'] = $sortBy;
        }

        $data = $this->executeGet(ApiPaths::CONVERSATIONS, $query);
        return ConversationListResponse::fromArray($data);
    }

    /**
     * 删除会话
     */
    public function deleteConversation(string $conversationId, string $user): SimpleResponse
    {
        $data = $this->executeDelete(
            ApiPaths::CONVERSATIONS . '/' . $conversationId,
            ['user' => $user]
        );
        return SimpleResponse::fromArray($data);
    }

    /**
     * 会话重命名
     */
    public function renameConversation(string $conversationId, string $name, bool $autoGenerate, string $user): Conversation
    {
        $data = $this->executePost(
            ApiPaths::CONVERSATIONS . '/' . $conversationId . ApiPaths::NAME,
            [
                'name' => $name,
                'auto_generate' => $autoGenerate,
                'user' => $user,
            ]
        );
        return Conversation::fromArray($data);
    }

    /**
     * 语音转文字
     */
    public function audioToText(string $filePath, string $user): AudioToTextResponse
    {
        if (!file_exists($filePath)) {
            throw new \InvalidArgumentException("File not found: {$filePath}");
        }

        $response = $this->httpClient->request('POST', $this->baseUrl . ApiPaths::AUDIO_TO_TEXT, [
            'multipart' => [
                [
                    'name' => 'file',
                    'contents' => fopen($filePath, 'r'),
                    'filename' => basename($filePath),
                ],
                [
                    'name' => 'user',
                    'contents' => $user,
                ],
            ],
        ]);

        $data = $this->handleResponse($response);
        return AudioToTextResponse::fromArray($data);
    }

    /**
     * 语音转文字（流）
     */
    public function audioToTextStream($inputStream, string $fileName, string $user): AudioToTextResponse
    {
        $response = $this->httpClient->request('POST', $this->baseUrl . ApiPaths::AUDIO_TO_TEXT, [
            'multipart' => [
                [
                    'name' => 'file',
                    'contents' => $inputStream,
                    'filename' => $fileName,
                ],
                [
                    'name' => 'user',
                    'contents' => $user,
                ],
            ],
        ]);

        $data = $this->handleResponse($response);
        return AudioToTextResponse::fromArray($data);
    }

    /**
     * 文字转语音
     */
    public function textToAudio(?string $messageId, ?string $text, string $user): string
    {
        $body = ['user' => $user];

        if ($messageId !== null) {
            $body['message_id'] = $messageId;
        }
        if ($text !== null) {
            $body['text'] = $text;
        }

        $response = $this->httpClient->request('POST', $this->baseUrl . ApiPaths::TEXT_TO_AUDIO, [
            'json' => $body,
        ]);

        return $response->getBody()->getContents();
    }

    /**
     * 获取应用元数据
     */
    public function getAppMeta(): AppMetaResponse
    {
        $data = $this->executeGet(ApiPaths::META);
        return AppMetaResponse::fromArray($data);
    }

    /**
     * 获取应用信息
     */
    public function getAppInfo(): AppInfoResponse
    {
        $data = $this->executeGet(ApiPaths::META);
        return AppInfoResponse::fromArray($data);
    }

    /**
     * 获取应用参数
     */
    public function getAppParameters(): AppParametersResponse
    {
        $data = $this->executeGet(ApiPaths::META . '/parameters');
        return AppParametersResponse::fromArray($data);
    }

    /**
     * 获取应用 WebApp 设置
     */
    public function getAppWebAppSettings(): AppWebAppSettingResponse
    {
        $data = $this->executeGet(ApiPaths::META . '/webapp-settings');
        return AppWebAppSettingResponse::fromArray($data);
    }

    /**
     * 获取标注列表
     */
    public function getAnnotations(?int $page = null, ?int $limit = null): AnnotationListResponse
    {
        $query = [];
        if ($page !== null) {
            $query['page'] = $page;
        }
        if ($limit !== null) {
            $query['limit'] = $limit;
        }

        $data = $this->executeGet(ApiPaths::APPS_ANNOTATIONS, $query);
        return AnnotationListResponse::fromArray($data);
    }

    /**
     * 创建标注
     */
    public function saveAnnotation(string $question, string $answer): Annotation
    {
        $data = $this->executePost(
            ApiPaths::APPS_ANNOTATIONS,
            [
                'question' => $question,
                'answer' => $answer,
            ]
        );
        return Annotation::fromArray($data);
    }

    /**
     * 更新标注
     */
    public function updateAnnotation(string $annotationId, string $question, string $answer): Annotation
    {
        $data = $this->executePatch(
            ApiPaths::APPS_ANNOTATIONS . '/' . $annotationId,
            [
                'question' => $question,
                'answer' => $answer,
            ]
        );
        return Annotation::fromArray($data);
    }

    /**
     * 删除标注
     */
    public function deleteAnnotation(string $annotationId): SimpleResponse
    {
        $data = $this->executeDelete(ApiPaths::APPS_ANNOTATIONS . '/' . $annotationId);
        return SimpleResponse::fromArray($data);
    }

    /**
     * 标注回复初始设置
     */
    public function annotationReply(string $action, string $embeddingProviderName, string $embeddingModelName, int $scoreThreshold): AnnotationReply
    {
        $data = $this->executePost(
            ApiPaths::APPS_ANNOTATIONS_REPLY . '/' . $action,
            [
                'embeddingProviderName' => $embeddingProviderName,
                'embeddingModelName' => $embeddingModelName,
                'scoreThreshold' => $scoreThreshold,
            ]
        );
        return AnnotationReply::fromArray($data);
    }

    /**
     * 查询标注回复初始设置任务状态
     */
    public function getAnnotationReply(string $action, string $jobId): AnnotationReply
    {
        $data = $this->executeGet(
            ApiPaths::APPS_ANNOTATIONS_REPLY . '/' . $action . '/status/' . $jobId
        );
        return AnnotationReply::fromArray($data);
    }

    /**
     * 获取对话变量
     */
    public function getConversationVariables(string $conversationId, string $user, ?string $lastId = null, ?int $limit = null, ?string $variableName = null): VariableResponse
    {
        $query = [
            'user' => $user,
            'limit' => $limit ?? 20,
        ];

        if ($lastId !== null) {
            $query['last_id'] = $lastId;
        }
        if ($variableName !== null) {
            $query['variable_name'] = $variableName;
        }

        $data = $this->executeGet(
            ApiPaths::CONVERSATIONS . '/' . $conversationId . '/variables',
            $query
        );
        return VariableResponse::fromArray($data);
    }

    /**
     * 上传文件
     */
    public function uploadFile($fileOrRequest, $filePathOrStream, ?string $user = null): FileUploadResponse
    {
        return parent::uploadFile($fileOrRequest, $filePathOrStream, $user);
    }

    /**
     * 预览文件
     */
    public function previewFile(string $fileId, bool $asAttachment = false): FilePreviewResponse
    {
        $data = $this->executeGet(ApiPaths::FILES_PREVIEW . '/' . $fileId, [
            'as_attachment' => $asAttachment
        ]);
        return FilePreviewResponse::fromArray($data);
    }

    /**
     * 处理流式响应
     */
    private function processStreamResponse($response, ChatStreamCallback $callback): void
    {
        $body = $response->getBody();

        while (!$body->eof()) {
            $line = $body->readLine();

            if (empty(trim($line))) {
                continue;
            }

            if (strpos($line, 'data: ') === 0) {
                $data = substr($line, 6);
                $eventData = json_decode($data, true);

                if ($eventData !== null) {
                    $event = EventFactory::create($eventData['event'] ?? 'unknown', $eventData);
                    $this->dispatchEvent($event, $callback);
                }
            } elseif (strpos($line, 'event: ping') === 0) {
                $callback->handlePing(new \Kunfeng\Dify\Event\PingEvent());
            }
        }
    }

    /**
     * 分发事件
     */
    private function dispatchEvent($event, ChatStreamCallback $callback): void
    {
        switch (get_class($event)) {
            case \Kunfeng\Dify\Event\MessageEvent::class:
                $callback->handleMessage($event);
                break;
            case \Kunfeng\Dify\Event\MessageEndEvent::class:
                $callback->handleMessageEnd($event);
                break;
            case \Kunfeng\Dify\Event\MessageFileEvent::class:
                $callback->handleMessageFile($event);
                break;
            case \Kunfeng\Dify\Event\TtsMessageEvent::class:
                $callback->handleTtsMessage($event);
                break;
            case \Kunfeng\Dify\Event\TtsMessageEndEvent::class:
                $callback->handleTtsMessageEnd($event);
                break;
            case \Kunfeng\Dify\Event\MessageReplaceEvent::class:
                $callback->handleMessageReplace($event);
                break;
            case \Kunfeng\Dify\Event\AgentMessageEvent::class:
                $callback->handleAgentMessage($event);
                break;
            case \Kunfeng\Dify\Event\AgentThoughtEvent::class:
                $callback->handleAgentThought($event);
                break;
            case \Kunfeng\Dify\Event\AgentLogEvent::class:
                $callback->handleAgentLog($event);
                break;
            case \Kunfeng\Dify\Event\ErrorEvent::class:
                $callback->handleError($event);
                break;
        }
    }
}
